/*
 *	workmanager.c
 *	© 2009 Tom Booth
 *
 *	A work manager for a set of worker threads
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#include "workmanager.h"

static struct wm_queue_item {
	char *payload;
	struct wm_queue_item* next;
};


static void wm_free_item(struct wm_queue_item *item);

// this variable hold the front of the queue
static struct wm_queue_item *queue_base;

// a mutex to guard against hurting our queue of work items
static pthread_mutex_t queue_mutex;

void wm_init_manager(void) {
	queue_base = NULL;
	pthread_cond_init(&wm_more_jobs, NULL);
	pthread_mutex_init(&wm_more_jobs_mutex, NULL);
	pthread_mutex_init(&queue_mutex, NULL);
}

void wm_destroy_manager(void) {
	wm_free_item(queue_base);
	
	pthread_cond_destroy(&wm_more_jobs);
	pthread_mutex_init(&wm_more_jobs_mutex, NULL);
	pthread_mutex_destroy(&queue_mutex);
}

void wm_add_item(char *payload) {
	struct wm_queue_item *item;
	
	// lock the queue
	pthread_mutex_lock(&queue_mutex);
	
	// find the first item with out a next
	// check if the base is null
	if (queue_base == NULL) {
		queue_base = malloc(sizeof(struct wm_queue_item));
		queue_base->payload = payload;
		queue_base->next = NULL;
	}
	else {
		item = queue_base;
		while (item->next != NULL) {item = item->next;}
		
		item->next = malloc(sizeof(struct wm_queue_item));
		item->next->payload = payload;
		item->next->next = NULL;
	}
	
	// unlock the queue
	pthread_mutex_unlock(&queue_mutex);
	
	// alert anyone sitting on the condition var
	pthread_cond_signal(&wm_more_jobs);
}



char *wm_get_item(void){
	
	struct wm_queue_item *hold;
	char *ret;
	
	// lock the queue
	pthread_mutex_lock(&queue_mutex);
	
	// check if the head is null
	if (queue_base== NULL) {
		pthread_mutex_unlock(&queue_mutex);
		return NULL;
	}
	
	// retrieve the current head and then set a new head
	hold = queue_base;
	ret = hold->payload;
	queue_base = hold->next;
	free(hold);
	
	// unlock the queue
	pthread_mutex_unlock(&queue_mutex);
	
	return ret;
	
}

int wm_item_avaliable() { 
	int ret;
	
	// lock
	pthread_mutex_lock(&queue_mutex);
	
	ret = (queue_base!=NULL);
	
	// unlock
	pthread_mutex_unlock(&queue_mutex);
	
	return ret; 
}



static void wm_free_item(struct wm_queue_item *item) {
	if (item==NULL) return;
	if (item->next!=NULL) wm_free_item(item->next);
	
	free(item->payload);
	free(item);
	
}





